import io.vertx.ext.web.client.WebClient
import io.vertx.kotlin.coroutines.CoroutineVerticle
import io.vertx.kotlin.coroutines.dispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

class MainVerticle : CoroutineVerticle() {
  enum class StatusNetWork {
    ONLINE, OFFLINE
  }

  enum class StatusUpdate {
    OK, FAILED
  }

  private var ip: String? = null
  private val minDuration: Long = 10000
  private var statusNetwork: StatusNetWork = StatusNetWork.ONLINE
  private var statusUpdate: StatusUpdate = StatusUpdate.OK
  private var retry: Int = 0
  private val logHelper: LogHelper by lazy {
    LogHelper(vertx.fileSystem())
  }

  private val ipHelper: IpHelper by lazy {
    val client = WebClient.create(vertx)
    IpHelper(client)
  }

  private val conf by lazy {
    vertx.fileSystem()
      .readFileBlocking("./config.json")
      .let { ConfObject(it) }
  }

  private val aliyunDomainHelper: AliyunDomainHelper by lazy {
    val accessKeyId = conf.accessKeyId
    val accessKeySecret = conf.accessKeySecret
    AliyunDomainHelper(accessKeyId, accessKeySecret)
  }

  private suspend fun update(ip: String) {
    try {
      aliyunDomainHelper.getRecord("${conf.rr}.${conf.domain}").forEach {
        if (ip != it.value) {
          aliyunDomainHelper.updateRecord(it.recordId, conf.rr, ip)
        }
      }
    } catch (e: Exception) {
      throw ExceptionUpdateRecordFailed(e.message)
    }
  }

  override suspend fun start() {
    logHelper.addLog("start.")
    val duration = if (conf.duration < minDuration) {
      minDuration
    } else {
      conf.duration
    }
    vertx.setPeriodic(duration) {
      CoroutineScope(vertx.dispatcher()).launch {
        try {
          when (statusNetwork) {
            StatusNetWork.ONLINE -> {
              val ipNew = ipHelper.getWanIp()
              if (ip == null || ip != ipNew) {
                if (statusUpdate == StatusUpdate.OK){
                  logHelper.addLog("ip:$ip -> $ipNew")
                }
                update(ipNew)
                statusUpdate = StatusUpdate.OK
                logHelper.addLog("update:ok")
                ip = ipNew
              }
            }
            StatusNetWork.OFFLINE -> {
              ipHelper.getWanIp()
              logHelper.addLog("network:online")
              statusNetwork = StatusNetWork.ONLINE
            }
          }
        } catch (e: ExceptionGetWanIpFailed) {
          if (statusNetwork == StatusNetWork.ONLINE) {
            logHelper.addLog("network:offline")
            statusNetwork = StatusNetWork.OFFLINE
          }
        } catch (e:ExceptionUpdateRecordFailed){
          if (statusUpdate == StatusUpdate.OK) {
            logHelper.addLog("update:failed,${e.message}")
            statusUpdate = StatusUpdate.FAILED
          }
        }
      }
    }
  }
}
